package ctx_parser

import (
	"gitee.com/tiger000/taishan_web/utils/conv"
	"github.com/bytedance/sonic/ast"
	"github.com/spf13/cast"
	"net/url"
	"strings"
)

// QueryStream query流式处理
type QueryStream struct {
	data    []byte
	pointer int
	eof     bool
}

func (that *QueryStream) Eof() bool {
	if that.pointer >= len(that.data) {
		that.eof = true
		return true
	}
	return false
}

func (that *QueryStream) Peek() byte {
	if !that.Eof() {
		return that.data[that.pointer]
	}
	return 0
}
func (that *QueryStream) Next() byte {
	if !that.Eof() {
		ch := that.data[that.pointer]
		that.pointer++
		return ch
	}
	return 0
}

// GetQuery 从query中获取参数
// 例如 a=1&b="zhangsan"&c=false&d[]=1&d[]=2
func GetQuery(query []byte, name string) string {
	if name == "" {
		return ""
	}
	length := len(query)

	value := ""
	for i := 0; i < length; i++ {
		if query[i] == name[0] && i+len(name)+1 < length {
			foundName := true
			for k := 1; k < len(name); k++ {
				if query[i+k] != name[k] {
					foundName = false
					break
				}
			}

			//没找到name,继续查找
			if !foundName {
				continue
			}
			//查找value
			firstEq := 0
			for v := i + len(name); v < length; v++ {
				if query[v] == '=' {
					firstEq = v
					continue
				}
				if query[v] == '&' {
					//找到value
					value = string(query[firstEq+1 : v])
					value, _ = url.QueryUnescape(value)
					if len(value) > 0 {
						if value[0] == '"' {
							value = value[1:]
						}
						if value[len(value)-1] == '"' {
							value = value[:len(value)-1]
						}
					}
					break
				}

				//到达末尾
				if v+1 == length {
					//找到value
					value = string(query[firstEq+1 : v+1])
					value, _ = url.QueryUnescape(value)
					if len(value) > 0 {
						if value[0] == '"' {
							value = value[1:]
						}
						if value[len(value)-1] == '"' {
							value = value[:len(value)-1]
						}
					}

					break
				}
			}
			break
		}

	}
	return value
}

func ConvertQueryMapBytesToBytes(data map[string][]byte) []byte {
	var result []byte
	for key, value := range data {
		if len(result) > 0 {
			result = append(result, '&')
		}
		result = append(result, conv.String2Bytes(key)...)
		result = append(result, '=')
		result = append(result, value...)
	}
	return result
}

// GetQueryMapBytes
// 将query对象转换为map[string][]byte结构
// 支持key为数组方式，将数组转换为json数组
func GetQueryMapBytes(query []byte) (result map[string][]byte) {
	length := len(query)
	result = make(map[string][]byte)
	EQIndex := 0 //kv中等号的位置
	kvIndex := 0 //kv开始的位置

	//临时存储数组
	var arrayValuesTemp = make(map[string]ArrayValue, 0)

	for i := 0; i < length; i++ {
		if query[i] == '=' && EQIndex == 0 {
			EQIndex = i
			continue
		}
		if i+1 == length || query[i+1] == '&' {
			//兼容设计：
			//1.没有找到=，直接放弃这段kv值
			//2.等号前没有key，直接放弃这段kv值
			if EQIndex == 0 || EQIndex <= kvIndex {
				i = i + 1
				kvIndex = i + 1
				EQIndex = 0
				continue
			}

			value := conv.Bytes2String(query[EQIndex+1 : i+1])
			value, _ = url.QueryUnescape(value)

			key, _ := url.QueryUnescape(conv.Bytes2String(query[kvIndex:EQIndex]))
			key = strings.Trim(key, " ")
			//fmt.Println(key)
			if len(key) == 0 {
				i = i + 1
				kvIndex = i + 1
				EQIndex = 0
				continue
			}
			//判断是否是数组
			if len(key) >= 3 && key[len(key)-2] == '[' && key[len(key)-1] == ']' {
				key = key[:len(key)-2]
				keyStr := key
				vType := valueType([]byte(value))
				if data, ok := arrayValuesTemp[keyStr]; ok {
					if vType > data.Type {
						data.Type = vType
					}
					data.Data = append(data.Data, []byte(value))
					arrayValuesTemp[keyStr] = data
				} else {
					data = ArrayValue{
						Key:  keyStr,
						Type: vType,
						Data: make([][]byte, 0),
					}
					data.Data = append(data.Data, []byte(value))
					arrayValuesTemp[keyStr] = data
				}

			} else {
				result[key] = []byte(value)
			}

			//重置i, kvIndex, EQIndex
			i = i + 1
			kvIndex = i + 1
			EQIndex = 0

			continue
		}

		if i == 0 && query[0] == '&' {
			kvIndex = 1
			EQIndex = 0
			continue
		}
	}

	//将数组转换为json对象

	for _, value := range arrayValuesTemp {
		var builder strings.Builder
		builder.WriteString("[")
		switch value.Type {
		case VBool, VFloat, VInt: //bool
			for key, data := range value.Data {
				if key > 0 {
					builder.WriteString(",")
				}
				builder.WriteString(conv.Bytes2String(data))
			}
		default:
			for key, data := range value.Data {
				if key > 0 {
					builder.WriteString(",")
				}
				builder.WriteString("\"")
				builder.WriteString(conv.Bytes2String(data))
				builder.WriteString("\"")
			}
		}
		builder.WriteString("]")

		result[value.Key] = []byte(builder.String())
	}

	return
}

// ArrayValue 存储数组对象
type ArrayValue struct {
	Key  string
	Type VType
	Data [][]byte
}

type VType uint8

const (
	VBool VType = iota
	VFloat
	VInt
	VString
)

// 获取数据类型
// 0 bool
// 1 float64
// 2 int64
// 3 string
func valueType(value []byte) VType {
	if len(value) == 4 && conv.Bytes2String(value) == "true" {
		return VBool
	} else if len(value) == 5 && conv.Bytes2String(value) == "false" {
		return VBool
	}

	stream := QueryStream{
		data:    value,
		pointer: 0,
		eof:     false,
	}

	if stream.Eof() {
		return VString
	}

	ch := stream.Peek()

	if isDigit(ch) {
		stream.Next()
		hasDot := false
		for !stream.Eof() {
			ch = stream.Peek()
			if isDigit(ch) {
				stream.Next()
				continue
			}
			if ch == '.' && hasDot == false {
				stream.Next()
				if stream.Eof() {
					return VString
				}
				ch = stream.Peek()
				if !isDigit(ch) {
					return VString
				}
				stream.Next()
				hasDot = true
			} else {
				return VString
			}

		}
		if hasDot {
			return VFloat
		}
		return VInt
	} else if ch == '-' {
		stream.Next()
		ch = stream.Peek()
		if isDigit(ch) {
			stream.Next()
			hasDot := false
			for !stream.Eof() {
				ch = stream.Peek()
				if isDigit(ch) {
					stream.Next()
					continue
				}
				if ch == '.' && hasDot == false {
					stream.Next()
					if stream.Eof() {
						return VString
					}
					ch = stream.Peek()
					if !isDigit(ch) {
						return VString
					}
					stream.Next()
					hasDot = true
				} else {
					return VString
				}

			}
			if hasDot {
				return VFloat
			}
			return VInt
		}
		return VString

	}
	return VString

}

func isDigit(ch byte) bool {
	return ch >= '0' && ch <= '9'
}

func GetQueryAst(query []byte) (result ast.Node) {
	length := len(query)

	EQIndex := 0 //kv中等号的位置
	kvIndex := 0 //kv开始的位置
	pairList := make([]ast.Pair, 0)

	//临时存储数组
	var arrayValuesTemp = make(map[string]ArrayValue, 0)

	for i := 0; i < length; i++ {
		if query[i] == '=' && EQIndex == 0 {
			EQIndex = i
			continue
		}
		if i+1 == length || query[i+1] == '&' {
			//兼容设计：
			//1.没有找到=，直接放弃这段kv值
			//2.等号前没有key，直接放弃这段kv值
			if EQIndex == 0 || EQIndex <= kvIndex {
				i = i + 1
				kvIndex = i + 1
				EQIndex = 0
				continue
			}

			value := string(query[EQIndex+1 : i+1])
			value, _ = url.QueryUnescape(value)

			key, _ := url.QueryUnescape(string(query[kvIndex:EQIndex]))
			key = strings.Trim(key, " ")
			//fmt.Println(key)
			if len(key) == 0 {
				i = i + 1
				kvIndex = i + 1
				EQIndex = 0
				continue
			}
			//判断是否是数组
			if len(key) >= 3 && key[len(key)-2] == '[' && key[len(key)-1] == ']' {
				key = key[:len(key)-2]
				keyStr := key
				vType := valueType([]byte(value))
				if data, ok := arrayValuesTemp[keyStr]; ok {
					if vType > data.Type {
						data.Type = vType
					}
					data.Data = append(data.Data, []byte(value))
					arrayValuesTemp[keyStr] = data
				} else {
					data = ArrayValue{
						Key:  keyStr,
						Type: vType,
						Data: make([][]byte, 0),
					}
					data.Data = append(data.Data, []byte(value))
					arrayValuesTemp[keyStr] = data
				}

			} else {
				node := ast.NewString(value)
				//result.Set(key, node)
				pair := ast.Pair{
					Key:   key,
					Value: node,
				}
				pairList = append(pairList, pair)
			}

			//重置i, kvIndex, EQIndex
			i = i + 1
			kvIndex = i + 1
			EQIndex = 0

			continue
		}

		if i == 0 && query[0] == '&' {
			kvIndex = 1
			EQIndex = 0
			continue
		}
	}

	//将数组转换为json对象
	for _, value := range arrayValuesTemp {
		switch value.Type {
		case VBool: //bool
			arrNodes := make([]ast.Node, 0)
			for _, data := range value.Data {
				arrNodes = append(arrNodes, ast.NewBool(cast.ToBool(data)))
			}
			arrNode := ast.NewArray(arrNodes)
			//result.Set(value.Key, arrNode)
			pair := ast.Pair{
				Key:   value.Key,
				Value: arrNode,
			}
			pairList = append(pairList, pair)
		case VFloat, VInt:
			arrNodes := make([]ast.Node, 0)
			for _, data := range value.Data {
				arrNodes = append(arrNodes, ast.NewNumber(string(data)))
			}
			arrNode := ast.NewArray(arrNodes)
			//result.Set(value.Key, arrNode)
			pair := ast.Pair{
				Key:   value.Key,
				Value: arrNode,
			}
			pairList = append(pairList, pair)
		default:
			arrNodes := make([]ast.Node, 0)
			for _, data := range value.Data {
				arrNodes = append(arrNodes, ast.NewString(string(data)))
			}
			arrNode := ast.NewArray(arrNodes)
			//result.Set(value.Key, arrNode)
			pair := ast.Pair{
				Key:   value.Key,
				Value: arrNode,
			}
			pairList = append(pairList, pair)
		}
	}

	result = ast.NewObject(pairList)
	return
}
